package com.slipper.system.schedule.service;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.slipper.exception.CommonException;
import com.slipper.util.Tool;
import com.slipper.system.schedule.dao.ScheduleDao;
import com.slipper.scheduled.ScheduledUtil;
import com.slipper.util.UUID;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import com.slipper.system.schedule.bo.ScheduleBO;
import com.slipper.core.transaction.ReadTransactional;
import com.slipper.core.transaction.WriteTransactional;

@Service
@ReadTransactional
public class ScheduleService {
    @Resource
    private ScheduleDao scheduleDao;

    public ScheduleBO queryBy(String id) {
        return scheduleDao.queryBy(id);
    }
    
    public PageInfo<ScheduleBO> queryPage(ScheduleBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(scheduleDao.queryList(bo));
    }
    
    @WriteTransactional
    public Integer save(ScheduleBO bo) {
        int result;
        updateExpression(bo);
        if (bo.getStatus() == null) {
            bo.setStatus(ScheduleBO.STATUS_PAUSE);
        }
        if (Tool.isNotBlank(bo.getId())) {
            bo.setUpdateTime(System.currentTimeMillis());
            result = scheduleDao.update(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            result = scheduleDao.insert(bo);
        }

        ScheduledUtil.createScheduleJob(bo);
        return result;
    }

    private void updateExpression(ScheduleBO bo) {
        if (ScheduleBO.TYPE_CUSTOM.equals(bo.getType())) {
            bo.setPeriod(null);
            bo.setTime(null);
        } else if (ScheduleBO.TYPE_EVERY_DAY.equals(bo.getType())) {
            if (Tool.isBlank(bo.getTime())) throw new CommonException("时间不能为空！");
            String[] times = bo.getTime().split(":");
            if (times.length != 3) throw new CommonException("时间错误！");
            bo.setPeriod(null);
            // 秒 分 时 * * ? *
            bo.setExpression(String.format(
                    "%d %d %d * * ? *",
                    Integer.parseInt(times[2]),
                    Integer.parseInt(times[1]),
                    Integer.parseInt(times[0])));
        } else if (ScheduleBO.TYPE_EVERY_WEEK.equals(bo.getType())) {
            if (Tool.isBlank(bo.getTime())) throw new CommonException("时间不能为空！");
            String[] times = bo.getTime().split(":");
            if (times.length != 3) throw new CommonException("时间错误！");
            if (Tool.isBlank(bo.getPeriod())) throw new CommonException("周期不能为空！");
            bo.setPeriod(Arrays.stream(bo.getPeriod().split(",")).sorted().collect(Collectors.joining(",")));
            String periods = Arrays.stream(bo.getPeriod().split(","))
                    .map(ScheduleService::weekToNumber)
                    .sorted()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));

            // 秒 分 时 * * 周 *
            bo.setExpression(String.format(
                    "%d %d %d ? * %s *",
                    Integer.parseInt(times[2]),
                    Integer.parseInt(times[1]),
                    Integer.parseInt(times[0]),
                    periods));
        } else if (ScheduleBO.TYPE_EVERY_MONTH.equals(bo.getType())) {
            if (Tool.isBlank(bo.getTime())) throw new CommonException("时间不能为空！");
            String[] times = bo.getTime().split(":");
            if (times.length != 3) throw new CommonException("时间错误！");
            if (Tool.isBlank(bo.getPeriod())) throw new CommonException("周期不能为空！");
            String periods = Arrays.stream(bo.getPeriod().split(","))
                    .map(Integer::parseInt)
                    .sorted()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            bo.setPeriod(periods);
            // 秒 分 时 日 * ? *
            bo.setExpression(String.format(
                    "%d %d %d %s * ? *",
                    Integer.parseInt(times[2]),
                    Integer.parseInt(times[1]),
                    Integer.parseInt(times[0]),
                    periods));
        }
    }

    public static int weekToNumber(String week) {
        return switch (week) {
            case "星期天" -> 1;
            case "星期一" -> 2;
            case "星期二" -> 3;
            case "星期三" -> 4;
            case "星期四" -> 5;
            case "星期五" -> 6;
            case "星期六" -> 7;
            default -> throw new IllegalArgumentException("无效星期");
        };
    }

    @WriteTransactional
    public void saveList(List<ScheduleBO> scheduleList) {
        if (scheduleList == null) return;

        scheduleList.forEach(schedule -> {
            if (scheduleDao.queryBy(schedule.getId()) != null) {
                schedule.setUpdateTime(System.currentTimeMillis());
                scheduleDao.update(schedule);
            } else {
                schedule.setUpdateTime(System.currentTimeMillis());
                schedule.setCreateTime(System.currentTimeMillis());
                scheduleDao.insert(schedule);
            }
        });
    }
    
    @WriteTransactional
    public Integer delete(ScheduleBO bo) {
        if (Tool.isNotBlank(bo.getId())) {
            ScheduledUtil.deleteScheduleJob(bo.getId());
        }
        for (String id : bo.getIds()) {
            ScheduledUtil.deleteScheduleJob(id);
        }
        return scheduleDao.delete(bo);
    }

    public List<ScheduleBO> queryList(ScheduleBO bo) {
        return scheduleDao.queryList(bo);
    }
    
    public int runTask(ScheduleBO bo) {
        for (String id : bo.getIds()) {
            ScheduledUtil.run(this.scheduleDao.queryBy(id));
        }
        return 1;
    }
    
    @WriteTransactional
    public int updateStatus(ScheduleBO bo) {
        for (String id : bo.getIds()) {
            ScheduleBO task = this.scheduleDao.queryBy(id);
            
            if (ScheduleBO.STATUS_NORMAL.equals(bo.getStatus())) { // 恢复任务
                ScheduledUtil.resumeJob(bo);
            } else if (ScheduleBO.STATUS_PAUSE.equals(bo.getStatus())) { // 暂停任务
                ScheduledUtil.pauseJob(task.getId());
            } else {
                continue;
            }
            task.setUpdateTime(System.currentTimeMillis());
            task.setStatus(bo.getStatus());
            this.scheduleDao.update(task);
        }

        return 1;
    }

    @WriteTransactional
    public void deleteByBusiness(ScheduleBO bo) {
        ScheduleBO qo = new ScheduleBO();
        qo.setBusinessType(bo.getBusinessType());
        qo.setBusinessId(bo.getBusinessId());
        scheduleDao.queryList(qo).forEach(item -> {
            ScheduledUtil.deleteScheduleJob(item.getId());
            scheduleDao.delete(item);
        });
    }

    @WriteTransactional
    public void updateByBusiness(ScheduleBO bo) {
        ScheduleBO qo = new ScheduleBO();
        qo.setBusinessType(bo.getBusinessType());
        qo.setBusinessId(bo.getBusinessId());
        scheduleDao.queryList(qo).stream().findFirst().ifPresent(item -> bo.setId(item.getId()));

        updateExpression(bo);
        if (bo.getStatus() == null) {
            bo.setStatus(ScheduleBO.STATUS_PAUSE);
        }
        if (Tool.isNotBlank(bo.getId())) {
            bo.setUpdateTime(System.currentTimeMillis());
            scheduleDao.update(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            scheduleDao.insert(bo);
        }

        ScheduledUtil.createScheduleJob(bo);
    }
}
